<?
/* 

	Date class
	provides an object oriented way to manipulate date and time
	

@BUGS
	As Date class uses Unix timetamp underlyingly, Date is only functionning on period :
	01 Jan 1970 00:00:00 +0000 to 19 Jan 2038 03:14:07 +0000
	
@VERSION 0.5


 */
 
define( 'FMT_DATEFR', '%d/%m/%Y' );
define( 'FMT_DATEUS', '%m/%d/%Y' );
define( 'FMT_DATEISO', '%Y%m%dT%H%M%S' );
define( 'FMT_DATELDAP', '%Y%m%d%H%M%SZ' );
define( 'FMT_DATEMYSQL', '%Y-%m-%d %H:%M:%S' );
define( 'FMT_DATERFC822', '%a, %d %b %Y %H:%M:%S' );
define( 'FMT_TIME', '%H:%M' );
define( 'WDAY_SUNDAY',    0 );
define( 'WDAY_MONDAY',    1 );
define( 'WDAY_TUESDAY',   2 );
define( 'WDAY_WENESDAY',  3 );
define( 'WDAY_THURSDAY',  4 );
define( 'WDAY_FRIDAY',    5 );
define( 'WDAY_SATURDAY',  6 );
define( 'SEC_MINUTE',    60 );
define( 'SEC_HOUR',    3600 );
define( 'SEC_DAY',    86400 );

class Date
{
	/* unix timestamp */
	var $ts;	
	var $Y;
	var $M;
	var $D;
	var $h;
	var $m;
	var $s; 
	/* @scope protected */
	var $change = 0; // 1 if date needs recalculation
		
	function Date( $ts = "" )
	{
		if( $ts ) {
			$this->setTimestamp($ts);
		} else {
			$this->setTimestamp(time());
		}
	}

	/****
	 *	Build a date from an ISO datetime string
	 * @params $datetime string the iso-X datetime with both date and time components
	 * @static @factory
	 * @return a Date object if ok, NULL otherwise
	 *
	 * tolerant: accepts format variants with or without separators: "-" for date and ":" for time
	 *	20010801123059 => OK
	 *	20010801T123059Z => OK
	 *	2001-08-01T12:30:59 => OK
	 *	20010801T12:30:59Z => OK
	 * 2001-08-01 => error
	 * 2001-08-01T01:30 => error
	 * 2001-08-01T1:30:59 => error
	 *	timezone code is yet ignored ( not handled )
	 */
	function fromDatetime( $datetime )
	{
		if( ! preg_match( "/^(\d{4})-?(\d{2})-?(\d{2})T?(\d{2}):?(\d{2}):?(\d{2})(.?)$/", $datetime, $a ) ) 
		{
			return NULL;
		}
		$obj = new Date();
		$obj->setDate( $a[1], $a[2], $a[3] );
		$obj->setTime( $a[4],$a[5], $a[6] );
		return $obj;
	}	

	function toString( $format )
	{
		return strftime( $format, $this->getTimestamp() );
	}
	
	/*
	 * can use as static form eg: Date::format( "%Y", $ts )
	 * @static
	 */
	function format( $format, $timestamp )
	{
		return strftime( $format, $timestamp );
	}

/**************************************************** GETTERS ****/
	
	function getYear() { 
		if( $this->change ) $this->_calc();
		return $this->Y; 
	}
	
	function getMonth() { 
		if( $this->change ) $this->_calc();
		return $this->M; 
	}
	
	function getDay() { 
		if( $this->change ) $this->_calc();
		return $this->D; 
	}

	function getWeekday() { 
		if( $this->change ) $this->_calc();
		return $this->weekday; 
	}

	function getYearDay()
	{
		if( $this->change )
			$this->_calc();
		return date( "z", $this->ts );
	}

	function getHours() { 
		if( $this->change ) $this->_calc();
		return $this->h; 
	}

	function getMinutes() { 
		if( $this->change ) $this->_calc();
		return $this->m; 
	}

	function getSeconds() { 
		if( $this->change ) $this->_calc();
		return $this->s; 
	}

	function getSecondsInDay() { 
		if( $this->change ) $this->_calc();
		$ts1 = mktime( 0,0,0, $this->M, $this->D, $this->Y );
		return $this->ts - $ts1; 
	}

	// return Unix timestamp (seconds since epoch )
	function getTimestamp() { 
		if( $this->change ) $this->_calc();
		return $this->ts; 
	}
	
	function daysInMonth()
	{
		if( $this->change )
			$this->_calc();
		return date( "t", $this->ts );
	}
	
	function daysInYear()
	{
		if( $this->change )
			$this->_calc();
		return date( "t", $this->ts );
	}
	
	function DaysTo( $date )
	{
		if( ! is_object($date) || get_class($date) != "date" )
			return false;
		$deltats = $date->getTimestamp() - $this->getTimestamp();
		if( $deltats > 0 )
			return (int) floor( $deltats / SEC_DAY );
		else
			return (int) ceil( $deltats / SEC_DAY );
	}
	
	function compareTo( $date )
	{
		if( ! is_object($date) || get_class($date) != "date" )
			return false;	
		return $this->getTimestamp() - $date->getTimestamp();
	}
	
	function addDays( $numdays )
	{
		$this->D += $numdays;
		$this->_calc();
	}

	function addMonths( $num )
	{
		$this->M += $num;
		$this->_calc();
	}
	
	function addYears( $num )
	{
		$this->Y += $num;
		$this->_calc();
	}
	
	function addHours( $num )
	{
		$this->h += $num;
		$this->_calc();
	}

	function addMinutes( $num )
	{
		$this->m += $num;
		$this->_calc();
	}
	
	function addSeconds( $num )
	{
		$this->s += $num;
		$this->_calc();
	}
			
/**************************************************** SETTERS ****/
	
	function setTimestamp( $ts )
	{
		// TODO : basic validation
		$this->ts = $ts;
		$a = getdate( $this->ts );
		$this->Y = $a['year'];
		$this->M = $a['mon'];
		$this->D = $a['mday'];
		$this->h = $a['hours'];
		$this->m = $a['minutes'];
		$this->s = $a['seconds'];
		$this->weekday = $a['wday'];
		$this->change = 0;
		unset($a);
	}

	function setDate( $Y, $M, $D=1 )
	{
		$this->Y = $Y;
		$this->M = $M;
		$this->D = $D;
		$this->change = 1;
	}

	function setTime( $h, $m, $s=0 )
	{
		$this->h = $h;
		$this->m = $m;
		$this->s = $s;
		$this->change = 1;
	}
	
	function setHours( $val )
	{
		$this->h = $val;
		$this->change = 1;
	}
	
	function setMinutes( $val )
	{
		$this->m = $val;
		$this->change = 1;
	}
	
	function setSeconds( $val )
	{
		$this->s = $val;
		$this->change = 1;
	}
	
	function setYear( $val )
	{
		$this->Y = $val;
		$this->change = 1;
	}

	function setMonth( $val )
	{
		$this->M = $val;
		$this->change = 1;
	}
	
	function setDay( $val )
	{
		$this->D = $val;
		$this->change = 1;
	}

	// setWeekday( [0-6] ) 
	function setWeekday( $weekday )
	{
		$this->D += ($weekday - $this->weekday);
		$this->change = 1;
	}

	function isValid()
	{
		if( ! checkdate( $this->M, $this->D, $this->Y ) )
			return false;
		if( $this->Y < 1970 || $this->Y > 2038 )
			return false;			
		if( $this->h < 0 || $this->h > 23 || $this->m < 0 || $this->m > 59 || $this->s < 0 || $this->s > 59 )
			return false;
		return true;
	}

	/**
	 *	@protected 
	 */
	function _calc()
	{
		$this->ts = mktime( $this->h, $this->m, $this->s, $this->M, $this->D, $this->Y );
		$a = getdate( $this->ts );
		$this->Y = $a['year'];
		$this->M = $a['mon'];
		$this->D = $a['mday'];
		$this->h = $a['hours'];
		$this->m = $a['minutes'];
		$this->s = $a['seconds'];
		$this->weekday = $a['wday'];
		$this->change = 0;
	}
		
}

?>
